#pragma once

#include <cassert>
#include "ReferenceCount.h"

template <class T>
class Pointer
{
public:
	inline Pointer( ) : m_ptr(0)
	{
		m_reference = 0;
	};
	inline Pointer( T* ptr ) : m_ptr( ptr )
	{
		if (m_ptr)
		{
			m_reference = new ReferenceCount();
			m_reference->Grab();
		}
	};
	inline Pointer( const Pointer<T>& p )
	{
		if (p.isValid())
		{
			m_ptr = p.m_ptr;
			m_reference = p.m_reference;
			m_reference->Grab();
		} else
		{
			m_ptr = 0;
			m_reference = 0;
		}
	};
	inline ~Pointer( )
	{
		if (m_ptr)
		{
			if (m_reference->Release() == 0)
			{
				delete m_ptr;
				delete m_reference;
				m_reference = 0;
				m_ptr = 0;
			}
		}
	};
	inline void operator =( T* o )
	{
		if (m_ptr)
		{
			if (m_reference->Release() == 0)
			{
				delete m_ptr;
				delete m_reference;
				m_reference = 0;
				m_ptr = 0;
			}
		}
		m_ptr = o;
		if (m_ptr)
		{
			m_reference = new ReferenceCount;
			m_reference->Grab();
		}
	};
	inline void operator =( const Pointer<T>& p )
	{
		if (m_ptr)
		{
			if (m_reference->Release() == 0)
			{
				delete m_ptr;
				delete m_reference;
				m_reference = 0;
				m_ptr = 0;
			}
		}
		m_ptr = p.m_ptr;
		m_reference = p.m_reference;
		if (m_ptr)
			m_reference->Grab();
	};
	inline T& operator *() const
	{
		assert( m_ptr != 0 && "Tried to * on a NULL");
		return *m_ptr;
	};
	inline T* operator ->() const
	{
		assert( m_ptr != 0 && "Tried to *-> on a NULL");
		return m_ptr;
	};
	inline operator T*() const
	{
		return m_ptr;
	};
	inline bool isValid() const
	{
		return (m_ptr != 0);
	};
	inline bool operator !()
	{
		return (!m_ptr);
	};
	inline bool operator ==( const Pointer<T>& p ) const
	{
		return (m_ptr == p.m_ptr); 
	};
	inline bool operator ==( const T* o ) const 
	{
		return (m_ptr == o);
	};
	inline ReferenceCount* getReference()
	{
		return m_reference;
	};
	inline void* getPointer()
	{
		return m_ptr;
	};
	inline void setReference( ReferenceCount* referenceCount )
	{
		m_reference = referenceCount;
	};
	inline void setPointer( void* pointer )
	{
		m_ptr = (T*)pointer;
	};
	inline void CANCEL()
	{
		m_ptr = 0;
		if (m_reference->Release() == 0)
		{
			delete m_reference;
			m_reference = 0;
		}
	}
protected:
	ReferenceCount* m_reference;
	T* m_ptr;
};

template<class T1, class T2>
void CopyPointer( Pointer<T1>& from, Pointer<T2>& to )
{
	to.setPointer(from.getPointer());
	to.setReference(from.getReference());
	to.getReference()->Grab();
};